package com.theten52.demo.load.util;

import com.theten52.demo.load.model.JavaLibraryLoadNameInfo;
import org.springframework.core.io.ClassPathResource;

import java.io.*;
import java.lang.reflect.Field;

/**
 * 加载本地方法库
 * 参考：org.apache.tomcat.jni.Library
 *
 * @author wangjin
 */
public class LoadLibraryUtil {

    public static final String LIBRARY_PATH_STR = "java.library.path";
    public static final String LIBRARY_SYS_FILED_STR = "sys_paths";
    public static final String LIBRARY_USR_FILED_STR = "usr_paths";

    private static void load(String libraryPath, String libraryName) {
        setLibraryPath(libraryPath);

        printLibraryPath();

        loadLibraryInternal(libraryName);
    }

    private static void loadLibraryInternal(String libraryName) {
        LibraryLoader.loadLibrary(libraryName);
        System.out.println("-------");
        System.out.println("already load library:" + libraryName);
        System.out.println("-------");
    }

    private static void printLibraryPath() {
        String libraryPathEnv = System.getenv(LIBRARY_PATH_STR);
        System.out.println("libraryPathEnv:" + libraryPathEnv);
        String libraryPathProperty = System.getProperty(LIBRARY_PATH_STR);
        System.out.println("libraryPathProperty:" + libraryPathProperty);
    }

    private static void setLibraryPath(String libraryPath) {
        try {
            String libraryPathKey = LIBRARY_PATH_STR;
            String libraryPathValue = System.getProperty(libraryPathKey);
            //https://blog.csdn.net/quqibing001/article/details/51201768
            System.setProperty(libraryPathKey, libraryPathValue + File.pathSeparator + libraryPath);
            printClassLoaderField(LIBRARY_SYS_FILED_STR, libraryPath);
            printClassLoaderField(LIBRARY_USR_FILED_STR, libraryPath);
        } catch (Exception e) {
            // do nothing for exception
            e.printStackTrace();
        }
    }

    private static void printClassLoaderField(String fieldName, String newLibraryPath) {
        try {
            Field pathField = ClassLoader.class.getDeclaredField(fieldName);
            pathField.setAccessible(true);
            Object o = pathField.get(null);
            if (o instanceof String[]) {
                String[] paths = (String[])o;
                String[] tempPaths = new String[paths.length + 1];
                System.arraycopy(paths, 0, tempPaths, 0, paths.length);
                if (newLibraryPath != null) {
                    tempPaths[paths.length] = newLibraryPath;
                }
                pathField.set(null, tempPaths);
                System.out.println("-------");
                for (String path : tempPaths) {
                    System.out.println(fieldName + ":" + path);
                }
                System.out.println("-------");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void loadLibrary(String originFile) {

        System.out.println("-------");
        System.out.println("class path originFile:" + originFile);

        JavaLibraryLoadNameInfo javaLibraryLoadNameInfo = new JavaLibraryLoadNameInfo();
        String prefix = javaLibraryLoadNameInfo.getPrefix();
        String suffix = javaLibraryLoadNameInfo.getSuffix();

        System.out.println("system library prefix:" + prefix);
        System.out.println("system library suffix:" + suffix);

        //用指定目录的方法创建临时文件
        try {
            File tempFile = File.createTempFile(prefix, suffix, null);

            String libraryDirPrefix = suffix.substring(1);

            writeFile(libraryDirPrefix, originFile, tempFile);
            System.out.println("library path:" + tempFile.getAbsolutePath());

            String name = getTempFileName(tempFile.getName(), prefix, suffix);
            System.out.println("while add library file name:" + name);

            load(tempFile.getParentFile().getAbsolutePath(), name);
            System.out.println("-------");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void writeFile(String libraryDirPrefix, String fileName, File tempFile) {
        ClassPathResource classPathResource = new ClassPathResource(libraryDirPrefix + File.separator + fileName);

        //1. 创建流
        try (InputStream in = new BufferedInputStream(classPathResource.getInputStream());
            OutputStream os = new BufferedOutputStream(new FileOutputStream(tempFile))) {

            //2. 读写数据
            byte[] buffer = new byte[2048];
            int len = in.read(buffer);
            while (len != -1) {
                os.write(buffer, 0, len);
                len = in.read(buffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getTempFileName(String name, String prefix, String suffix) {
        return name.substring(0, name.lastIndexOf(suffix)).replaceFirst(prefix, "");
    }
}
